import javax.swing.*;
import javax.swing.event.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;

import commands.*;
import room.*;
import game.*;
import game.io.*;

public class TextAdventureGame {
  static Game myGame;
  
  static ActionCommand windowTwo = null;
  
  private static final int WINDOW_WIDTH = 375;

  private static final int WINDOW_HEIGHT = 375;

  private JFrame window = new JFrame("TAGEdit");

  private Container c = window.getContentPane();

  private JPanel pTop = new JPanel();

  private JPanel pBottom = new JPanel(new BorderLayout());

  private JMenuBar mainMenu = new JMenuBar();

  private JMenu menu = new JMenu("File");

  private JMenuItem newMenu = new JMenuItem("New...", KeyEvent.VK_N);

  private JMenuItem saveMenu = new JMenuItem("Save", KeyEvent.VK_S);

  private JMenuItem saveAsMenu = new JMenuItem("Save As...", KeyEvent.VK_A);

  private JMenuItem newRoom = new JMenuItem("Room", KeyEvent.VK_R);

  private JMenuItem newState = new JMenuItem("State", KeyEvent.VK_T);

  private JMenuItem removeRoom = new JMenuItem("Remove Room", KeyEvent.VK_E);

  private JMenuItem removeState = new JMenuItem("Remove State", KeyEvent.VK_M);

  private JMenuItem setDefaultRoom = new JMenuItem("Set Starting Room", KeyEvent.VK_S);

  private JMenuItem setDefaultState = new JMenuItem("Set Room's Start State", KeyEvent.VK_O);

  private JComboBox room = new JComboBox();

  private JComboBox state = new JComboBox();

  private JComboBox editing = new JComboBox();

  private JTextArea mainEdit = new JTextArea();

  private JScrollPane editorScrollPane = new JScrollPane(mainEdit);

  private JButton addAction = new JButton("New Action");

  private JFileChooser fc = new JFileChooser();

  private File fileOne;

  private Room currentRoom;

  private RoomState currentState;

  private Commands temp = new Commands();
  
  private TagIO tagio = new TagIO();
  
  public TextAdventureGame() {
    window.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
    window.setDefaultCloseOperation(window.DO_NOTHING_ON_CLOSE);
    window.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent we) {
        if(onExit(window))System.exit(0);
      }
    });
    menu.setMnemonic(KeyEvent.VK_F);
    newMenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,ActionEvent.CTRL_MASK));
    newMenu.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {

        if(c.isVisible() == false) {
          c.setVisible(true);
          saveMenu.setEnabled(true);
          saveAsMenu.setEnabled(true);
          newRoom.setEnabled(true);
          newState.setEnabled(true);
          removeRoom.setEnabled(true);
          removeState.setEnabled(true);
          setDefaultRoom.setEnabled(true);
          setDefaultState.setEnabled(true);
        }else{

          if(!onExit(window))return;
          room.removeAllItems();
          state.removeAllItems();
          editing.removeAllItems();
          mainEdit.setText("");
        }
        fileOne = null;
        window.setTitle("Adventure 0 - TAGEdit");
        myGame = new Game();
        currentRoom = myGame.createRoom();
        myGame.setCurrentRoom(currentRoom);
        currentState = currentRoom.getCurrentState();
        temp = currentState.addVerb("look");
        currentState.addVerb("north");
        currentState.addVerb("south");
        currentState.addVerb("east");
        currentState.addVerb("west");
        currentRoom.setName("Room 0");
        currentState.setDescription("State 0");
        room.addItem("Room 0");
        state.addItem("State 0");
        editing.addItem("Look");
        editing.addItem("North");
        editing.addItem("South");
        editing.addItem("East");
        editing.addItem("West");
      }
    });
    menu.add(newMenu);
    newMenu = new JMenuItem("Open...", KeyEvent.VK_O);
    newMenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,ActionEvent.CTRL_MASK));
    newMenu.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        fc.setDialogTitle("Open");
        //fc.setFileFilter(new TagFilter());
        fc.setAcceptAllFileFilterUsed(false);

        if (fc.showOpenDialog(window) == JFileChooser.APPROVE_OPTION) {

          while (!fc.getSelectedFile().exists() || !fc.getSelectedFile().getName().toLowerCase().endsWith(".tag")) {

            if (fc.showOpenDialog(window) != JFileChooser.APPROVE_OPTION)return;
          }
          File fileTwo = fc.getSelectedFile();

          if (!onExit(window))return;
          fileOne = fileTwo;
          room.removeAllItems();
          state.removeAllItems();
          editing.removeAllItems();
          window.setTitle(fileOne.getName() + " - TAGEdit");
          c.setVisible(true);
          saveMenu.setEnabled(true);
          saveAsMenu.setEnabled(true);
          newRoom.setEnabled(true);
          newState.setEnabled(true);
          removeRoom.setEnabled(true);
          removeState.setEnabled(true);
          setDefaultRoom.setEnabled(true);
          setDefaultState.setEnabled(true);
          myGame = tagio.XMLin(fileOne);
          
          for(int counterbean = 0;counterbean < myGame.getRooms().size();counterbean++){
            room.addItem(myGame.getRooms().get(counterbean).getName());
          }
          currentRoom = myGame.getCurrentRoom();
          room.setSelectedItem(currentRoom.getName());
          
          for(int counterbean = 0;counterbean < currentRoom.getStates().size();counterbean++){
            state.addItem(currentRoom.getStates().get(counterbean).getDescription());
          }
          currentState = currentRoom.getCurrentState();
          state.setSelectedItem(currentState.getDescription());
          temp = currentState.getVerbCommands("look");
          editing.addItem("Look");
          editing.addItem("North");
          editing.addItem("South");
          editing.addItem("East");
          editing.addItem("West");
          editing.setSelectedItem("Look");
          mainEdit.setText(temp.get(0).run());
        }
      }
    });
    menu.add(newMenu);
    menu.addSeparator();
    saveMenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,ActionEvent.CTRL_MASK));
    saveMenu.setEnabled(false);
    saveMenu.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {

        if (fileOne == null) {
          fc.setDialogTitle("Save");

          if (saveFile(window)) {

            while (fileOne.exists()) {

              if (JOptionPane.showConfirmDialog(null,fileOne.getAbsoluteFile() + " already exists.\nDo you want to replace it?","Save As",JOptionPane.YES_NO_OPTION,JOptionPane.WARNING_MESSAGE) != JOptionPane.YES_OPTION) {

                if (!saveFile(window)) {
                  fileOne = null;
                  return;
                }
              } else {
                break;
              }
            }
            window.setTitle(fileOne.getName() + " - TAGEdit");
            c.setVisible(true);
            tagio.XMLout(myGame,fileOne);
          }
        } else {
          tagio.XMLout(myGame,fileOne);
        }
      }
    });
    menu.add(saveMenu);
    saveAsMenu.setEnabled(false);
    saveAsMenu.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        File fileTwo = fileOne;

        fc.setDialogTitle("Save As");

        if (saveFile(window)) {

          while (fileOne.exists()) {

            if (JOptionPane.showConfirmDialog(null,fileOne.getAbsoluteFile() + " already exists.\nDo you want to replace it?","Save As", JOptionPane.YES_NO_OPTION,JOptionPane.WARNING_MESSAGE) != JOptionPane.YES_OPTION) {

              if (!saveFile(window)) {
                fileOne = fileTwo;
                return;
              }
            } else {
              break;
            }
          }
          window.setTitle(fileOne.getName() + " - TAGEdit");
          c.setVisible(true);
          tagio.XMLout(myGame,fileOne);
        }
      }
    });
    menu.add(saveAsMenu);
    menu.addSeparator();
    newMenu = new JMenuItem("Exit", KeyEvent.VK_X);
    newMenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4,ActionEvent.ALT_MASK));
    newMenu.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        if (onExit(window))System.exit(0);
      }
    });
    menu.add(newMenu);
    mainMenu.add(menu);
    menu = new JMenu("New");
    menu.setMnemonic(KeyEvent.VK_N);
    newMenu = new JMenuItem("Command...", KeyEvent.VK_M);
    newMenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M,ActionEvent.CTRL_MASK));
    newMenu.setEnabled(false);
    menu.add(newMenu);
    newMenu = new JMenuItem("Item", KeyEvent.VK_I);
    newMenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I,ActionEvent.CTRL_MASK));
    newMenu.setEnabled(false);
    menu.add(newMenu);
    newRoom.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R,ActionEvent.CTRL_MASK));
    newRoom.setEnabled(false);
    newRoom.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        currentRoom = myGame.createRoom();
        currentState = currentRoom.getCurrentState();
        temp = currentState.addVerb("look");
        currentState.addVerb("north");
        currentState.addVerb("south");
        currentState.addVerb("east");
        currentState.addVerb("west");
        currentRoom.setName("Room " + currentRoom.getId());
        currentState.setDescription("State 0");
        room.addItem("Room " + currentRoom.getId());
        room.setSelectedItem("Room " + currentRoom.getId());
        mainEdit.setText("");
        state.removeAllItems();
        state.addItem("State 0");
        editing.setSelectedItem("Look");
      }
    });
    menu.add(newRoom);
    newState.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T,ActionEvent.CTRL_MASK));
    newState.setEnabled(false);
    newState.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        currentState = currentRoom.createRoomState();
        temp = currentState.addVerb("look");
        currentState.addVerb("north");
        currentState.addVerb("south");
        currentState.addVerb("east");
        currentState.addVerb("west");
        currentState.setDescription("State " + (currentRoom.getStates().size() - 1));
        state.addItem(currentState.getDescription());
        state.setSelectedItem(currentState.getDescription());
        mainEdit.setText("");
        editing.setSelectedItem("Look");
      }
    });
    menu.add(newState);
    newMenu = new JMenuItem("Character", KeyEvent.VK_C);
    newMenu.setEnabled(false);
    menu.add(newMenu);
    menu.addSeparator();
    removeRoom.setEnabled(false);
    removeRoom.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {

        if (myGame.getRooms().size() == 1) {
          JOptionPane.showConfirmDialog(null,"You cannot delete the room.\nYou must maintain at least one room.","TAGEdit", JOptionPane.PLAIN_MESSAGE,JOptionPane.INFORMATION_MESSAGE);
        } else {

          if (currentRoom.getId() == myGame.getCurrentRoom().getId()) {

            if (myGame.getRooms().size() == 2) {

              if (myGame.getRooms().get(0).getName().equals(currentRoom.getName())) {
                myGame.setCurrentRoom(myGame.getRooms().get(1));
              } else {
                myGame.setCurrentRoom(myGame.getRooms().get(0));
              }
            } else {
              JOptionPane.showConfirmDialog(null,"You cannot delete the starting room.\nYou must set another room as the starting room before performing this opperation.","TAGEdit",JOptionPane.PLAIN_MESSAGE,JOptionPane.INFORMATION_MESSAGE);
              return;
            }
          }

          if (JOptionPane.showConfirmDialog(null,"Are you sure that you wish to delete " + currentRoom.getName() + "?", "TAGEdit",JOptionPane.YES_NO_OPTION,JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) {
            room.removeItem(currentRoom.getName());
            room.setSelectedItem(myGame.getCurrentRoom().getName());
            myGame.deleteRoom(currentRoom.getId());
            currentRoom = myGame.getCurrentRoom();
            currentState = currentRoom.getCurrentState();
            temp = currentState.getVerbCommands("look");
            state.setSelectedItem(currentState.getDescription());
            editing.setSelectedItem("Look");
            mainEdit.setText("");

            if (temp.size() > 0)mainEdit.setText(((Say) temp.getFirst()).getResponse());
          }
        }
      }
    });
    menu.add(removeRoom);
    removeState.setEnabled(false);
    removeState.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {

        if (currentRoom.getStates().size() == 1) {
          JOptionPane.showConfirmDialog(null,"You cannot delete the state.\nYou must maintain at least one state per room.","TAGEdit", JOptionPane.PLAIN_MESSAGE,JOptionPane.INFORMATION_MESSAGE);
        } else {

          if (currentState.getDescription().equals(currentRoom.getCurrentState().getDescription())) {

            if (currentRoom.getStates().size() == 2) {

              if (currentRoom.getStates().get(0).getDescription().equals(currentState.getDescription())) {
                currentRoom.setCurrentState(currentRoom.getStates().get(1));
              } else {
                currentRoom.setCurrentState(currentRoom.getStates().get(0));
              }
            } else {
              JOptionPane.showConfirmDialog(null,"You cannot delete the default state of a room.\nYou must set another state to the default and then perform this opperation.","TAGEdit",JOptionPane.PLAIN_MESSAGE,JOptionPane.INFORMATION_MESSAGE);
              return;
            }
          } else {

            if (JOptionPane.showConfirmDialog(null,"Are you sure that you wish to delete " + currentState.getDescription() + "?","TAGEdit", JOptionPane.YES_NO_OPTION,JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) {
              state.removeItem(currentState.getDescription());
              state.setSelectedItem(currentRoom.getCurrentState().getDescription());
              currentRoom.getStates().remove(currentState);
              currentState = currentRoom.getCurrentState();
              temp = currentState.getVerbCommands("look");
              editing.setSelectedItem("Look");
              mainEdit.setText("");

              if (temp.size() > 0)mainEdit.setText(((Say) temp.getFirst()).getResponse());
            }
          }
        }
      }
    });
    menu.add(removeState);
    setDefaultRoom.setEnabled(false);
    setDefaultRoom.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        myGame.setCurrentRoom(currentRoom);
      }
    });
    menu.add(setDefaultRoom);
    setDefaultState.setEnabled(false);
    setDefaultState.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        currentRoom.setCurrentState(currentState);
      }
    });
    menu.add(setDefaultState);
    mainMenu.add(menu);
    window.setJMenuBar(mainMenu);
    c.setLayout(new BorderLayout());
    c.setVisible(false);
    pTop.setLayout(new BoxLayout(pTop, BoxLayout.LINE_AXIS));
    c.add("North", pTop);
    room.setEditable(true);
    room.setPreferredSize(new Dimension(window.getWidth() / 3 - 10, 25));
    room.getEditor().getEditorComponent().addFocusListener(new FocusListener() {
          public void focusGained(FocusEvent e) {}
          
          public void focusLost(FocusEvent e) {
            String holder = ((JTextField) room.getEditor().getEditorComponent()).getText();
            String loser = currentRoom.getName();
            
            if (holder.equals(loser))return;
            room.addItem(holder);
            room.removeItem(loser);
            room.setSelectedItem(holder);
            currentRoom.setName(holder);
          }
        });
    room.addPopupMenuListener(new PopupMenuListener() {
      public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
        String holder = ((JTextField) room.getEditor().getEditorComponent()).getText();
        String loser = currentRoom.getName();

        if (holder.equals(loser))return;
        room.addItem(holder);
        room.removeItem(loser);
        room.setSelectedItem(holder);
        currentRoom.setName(holder);
      }

      public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
        if(!room.getSelectedItem().toString().equals(currentRoom.getName())) {
          currentRoom = myGame.getRoom(findRoom(room.getSelectedItem().toString()));
          state.removeAllItems();

          for(int counterbean = 0; counterbean < currentRoom.getStates().size(); counterbean++) {
            state.addItem(currentRoom.getStates().get(counterbean).getDescription());
          }
          currentState = currentRoom.getCurrentState();
          state.setSelectedItem(currentState.getDescription());
          temp = currentState.getVerbCommands("look");
          mainEdit.setText("");

          if(temp.size() > 0)mainEdit.setText(((Say)temp.getFirst()).getResponse());
          editing.setSelectedItem("Look");
        }
      }

      public void popupMenuCanceled(PopupMenuEvent e) {}
    });
    room.getEditor().getEditorComponent().addKeyListener(new KeyAdapter() {
      public void keyPressed(KeyEvent ke) {
        room.hidePopup();
      }
    });
    pTop.add(room);
    pTop.add(Box.createRigidArea(new Dimension(5, 0)));
    state.setEditable(true);
    state.setPreferredSize(new Dimension(window.getWidth() / 3 - 10, 25));
    state.getEditor().getEditorComponent().addFocusListener(new FocusListener() {
          public void focusGained(FocusEvent e) { }

          public void focusLost(FocusEvent e) {
            String holder = ((JTextField) state.getEditor().getEditorComponent()).getText();
            String loser = currentState.getDescription();

            if (holder.equals(loser))return;
            state.addItem(holder);
            state.removeItem(loser);
            state.setSelectedItem(holder);
            currentState.setDescription(holder);
          }
        });
    state.addPopupMenuListener(new PopupMenuListener() {
      public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
        String holder = ((JTextField) state.getEditor().getEditorComponent()).getText();
        String loser = currentState.getDescription();

        if (holder.equals(loser))return;
        state.addItem(holder);
        state.removeItem(loser);
        state.setSelectedItem(holder);
        currentState.setDescription(holder);
      }

      public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
        currentState = currentRoom.getStates().get(findState(state.getSelectedItem().toString()));
        temp = currentState.getVerbCommands("look");
        mainEdit.setText("");

        if(temp.size() > 0)mainEdit.setText(((Say)temp.getFirst()).getResponse());
        editing.setSelectedItem("Look");
      }

      public void popupMenuCanceled(PopupMenuEvent e) {}
    });
    state.getEditor().getEditorComponent().addKeyListener(new KeyAdapter() {
      public void keyPressed(KeyEvent ke) {
        state.hidePopup();
      }
    });
    pTop.add(state);
    pTop.add(Box.createRigidArea(new Dimension(5, 0)));
    editing.setPreferredSize(new Dimension(window.getWidth() / 3 - 10, 25));
    editing.addPopupMenuListener(new PopupMenuListener() {
      public void popupMenuWillBecomeVisible(PopupMenuEvent e) {}

      public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
        temp = currentState.getVerbCommands(editing.getSelectedItem().toString().toLowerCase());
        mainEdit.setText("");

        if (temp.size() > 0)mainEdit.setText(((Say) temp.getFirst()).getResponse());
      }

      public void popupMenuCanceled(PopupMenuEvent e) {}
    });
    pTop.add(editing);
    editorScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
    mainEdit.setWrapStyleWord(true);
    mainEdit.setLineWrap(true);
//    This will check if the verb already has a Command list. If it doesn't one will be created.
//    If it does the first Say Command will be modified to contain the text in mainEdit.
    mainEdit.addFocusListener(new FocusListener() {
      public void focusGained(FocusEvent e) {}
      public void focusLost(FocusEvent e) {
        
        if(temp.size() == 0){
          temp.add(new Say(mainEdit.getText() + "\n"));
        }else{
          ((Say)temp.getFirst()).setResponse(mainEdit.getText() + "\n");
        }
      }
    });
    c.add("Center", editorScrollPane);
    c.add("South", pBottom);
    addAction.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        windowTwo = new ActionCommand(currentRoom,currentState,editing.getSelectedItem().toString());
      }
    });
    pBottom.add("East", addAction);
    window.setVisible(true);
  }

  private int findRoom(String target) {

    for (int counterbean = 0; counterbean < myGame.getRooms().size(); counterbean++) {

      if (myGame.getRooms().get(counterbean).getName().equals(target))return myGame.getRooms().get(counterbean).getId();
    }
    return 0;
  }

  private int findState(String target) {
    
    for (int counterbean = 0; counterbean < currentRoom.getStates().size(); counterbean++) {
      
      if (currentRoom.getStates().get(counterbean).getDescription().equalsIgnoreCase(target))return counterbean;
    }
    return 0;
  }

  private boolean onExit(JFrame forPass) {
    
    if (!c.isVisible())return true;

    String dialogText;

    if (fileOne == null) {
      dialogText = "Do you want to save the changes to Adventure 0?";
    } else {
      dialogText = "Do you want to save the changes to " + fileOne.getName() + "?";
    }

    switch (JOptionPane.showConfirmDialog(null, dialogText, "TAGEdit",JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE)) {
      case JOptionPane.NO_OPTION:
        return true;
      case JOptionPane.YES_OPTION:

        if (fileOne == null) {
          fc.setDialogTitle("Save");

          if (!saveFile(forPass))return false;

          while (fileOne.exists()) {

            if (JOptionPane.showConfirmDialog(null, fileOne.getAbsoluteFile() + " already exists.\nDo you want to replace it?","Save As", JOptionPane.YES_NO_OPTION,JOptionPane.WARNING_MESSAGE) != JOptionPane.YES_OPTION) {

              if (!saveFile(window)) {
                fileOne = null;
                return false;
              }
            } else {
              break;
            }
          }
          tagio.XMLout(myGame,fileOne);
        } else {
          tagio.XMLout(myGame,fileOne);
        }
        return true;
      default:
        return false;
    }
  }

  private boolean saveFile(JFrame window) {
    int choice;
    //fc.setFileFilter(new TagFilter());
    fc.setAcceptAllFileFilterUsed(false);
    choice = fc.showSaveDialog(window);

    if (choice == JFileChooser.APPROVE_OPTION) {

      if (!fc.getSelectedFile().getAbsolutePath().toLowerCase().endsWith(".tag")) {
        fileOne = new File(fc.getSelectedFile().getAbsolutePath() + ".tag");
      } else {
        fileOne = fc.getSelectedFile();
      }
      return true;
    }
    return false;
  }

  public static void main(String[] args) {
    TextAdventureGame gui = new TextAdventureGame();
  }
}